Ну вот и подходит к концу второй модуль, осталось самое важное задание! Любый уважающий себя исследователь должен уметь нарисовать трехмерный чайник в R!
Датасет (Можно найти по ссылке) содержит информацию о полигонах трехмерной модели чайника. Три столбца таблицы задают координаты (x, y, z), а тройки строк задают треугольники (т. е. строки 1, 2, 3 - первый треугольник, 4, 5, 6 - второй, и так далее).
Напишите функцию, которая будет принимать data.table с этими данными, и возвращать объект plotly с трехмерной моделью. Следует воспользоваться установкой индексов i, j, k.
В данной статье я постараюсь пошагово объяснить и нагялдно показать, как “нарисовать” чайник при помощи языка R.
Итак, для начала подключим необходимые библиотеки, это “plotly” и “data.table”
library ('plotly')
library ('data.table')
Затем загрузим наш датасет. Важно, чтобы он находился в рабочей директории, иначе, при загрузке, путь к нему нужно будет прописывать вручную.
teapot <- read.csv ('teapot.csv', sep = ";")
Можно посмотреть стуруктуру датасета, однако, там и без того всё понятно, тем более, что переменные уже находятся в нужном виде. Однако, если по какой-либо причине необходимо, к примеру, поменять тип переменных, то тогда, сначала смотрим стурктуру, затем работаем с переменными.
Зададим координаты полигонов, из которых, впоследствии, будет собрана фигура.
mesh <- data.table (x = rnorm (40),
y = rnorm (40),
z = rnorm (40))
Координаты указаны, теперь необходимо задать векторы, согласно которым данные полигоны будут “уложены”.
i = teapot [1 : 3, 1]
j = teapot [1 : 3, 2]
k = teapot [1 : 3, 3]
Построим трёхмерную координатную сетку, в которой будет отображена фигура
plot_ly (teapot [1 : 3, ], type = "mesh3d",
x = ~x, y = ~y, z = ~z,
i = ~i, j = ~j, k = ~k)
Важно помнить, что для корректной работы кода при указании координат необходимо использовать тильду, однако, при проверке кода с помощью он-лайн средств, таких как “stdin → stdout”, тильды необходимо убирать.
Теперь следует указать сами полигоны, которые составят фигуру
i.s <- seq (from = 0, to = nrow (teapot) - 1, by = 3)
j.s <- seq (from = 1, to = nrow (teapot), by = 3)
k.s <- seq (from = 2, to = nrow (teapot), by = 3)
Функция “seq” генерирует последовательность чисел от 0 для вектора i (соответственно, 1 для j и 2 для k) до количества строк в нашем датасете с шагом в 3. Иными словами, каждые три строки датасета определяют один полигон.
Построим фигуру, используя функцию “plot_ly” библиотеки “plotly”.
plot_ly (teapot,
x = ~x, y = ~y, z = ~z,
i = ~i.s, j = ~j.s, k = ~k.s,
type = "mesh3d")
На выходе получаем тот самый чайник, который обязан уметь рисовать любой исследователь.
Осталось только “запаковать” всё это в функцию, согласно условияю задачи и выводить результат по команде.
make.fancy.teapot <- function (teapot.coords) {
i.s <- seq (from = 0, to = nrow (teapot.coords) - 1, by = 3)
j.s <- seq (from = 1, to = nrow (teapot.coords), by = 3)
k.s <- seq (from = 2, to = nrow (teapot.coords), by = 3)
plot_ly (teapot.coords,
x = ~x, y = ~y, z = ~z,
i = ~i.s, j = ~j.s, k = ~k.s,
type = "mesh3d")
}
make.fancy.teapot (teapot)
По хорошему, согласно условию задачи, следовало бы датасет конвертировать в дататейбл, однако, не вижу в этом необходимости, поскольку код и без него прекрасно работает.
Почти любая задача имеет больше одного пути решения, особенно если это касается языков программирования, кода и всех таких делов. Данный случай не является исключением и ниже я привожу ещё два варианта решения. Не вижу смысла столь подробно описывать свои действия, ведь если разобраться, то решение аналогично, просто выполнено при помощи несколько иных функций.
Итак…
make.fancy.teapot <- function (teapot.coords) {
is <- (1 : (nrow (teapot.coords) / 3)) * 3 - 3
js <- (1 : (nrow (teapot.coords) / 3)) * 3 - 2
ks <- (1 : (nrow (teapot.coords) / 3)) * 3 - 1
plot_ly (teapot.coords,
x = ~x,
y = ~y,
z = ~z,
i = ~is,
j = ~js,
k = ~ks, type = 'mesh3d')
}
make.fancy.teapot (teapot)
Данное решение было предложено обучающей платформой Stepik (оно появляется при условии правильного выполнения задания), на которой, собственно, и была размещена данная задача, как проверка, насколько хорошо студенты усвоили материал курса. Оригинальный текст решения выглядит так:
make.fancy.teapot <- function(teapot.coords) {
is <- (1 : (nrow (teapot.coords) / 3)) * 3 - 3
js <- (1 : (nrow (teapot.coords) / 3)) * 3 - 2
ks <- (1: (nrow (teapot.coords) / 3)) * 3 - 1
plot_ly (teapot.coords, x = x, y = y, z = z,
i = is,
j = js,
k = ks, type='mesh3d')
}
Но при исполнении этого кода в RStudio приведёт к ошибке:
## Error in plot_ly(teapot.coords, x = x, y = y, z = z, i = is, j = js, k = ks, : объект 'x' не найден
Всё дело в том, что сайт и компилятор (в частности, RStudio) используют разные версии языка, и отсутствие тильды в коде координат векторов и полигонов имеет критическое значение. По правде сказать, я сам не один час потратил на выявление проблемы, пока, наконец, не залез в комментарии, где добрые люди, столкнувшиеся с данной проблемой, не указали на необходимость удалить тильды при вставке кода в поле ответа на сайте.
make.fancy.teapot <- function (teapot.coords) {
ind <- data.table (i = seq (0, nrow (teapot.coords) -1, by = 3),
j = seq (1, nrow (teapot.coords) -1, by = 3),
k = seq (2, nrow (teapot.coords) -1, by = 3))
plot_ly (teapot.coords,
x = teapot.coords$x,
y = teapot.coords$y,
z = teapot.coords$z,
i = ind$i,
j = ind$j,
k = ind$k,
type = "mesh3d")
}
Это моё решение, однако, я не стал указывать его в качестве основного из-за того, что там более наглядно показаны все преобразования и все действия. По сути, эти два решения аналогичны, но здесь я указал координаты векторов и полигонов явно.
Лично я не могу достоверно сказать, какое прикладное значение имеет данная задача, но она очень хорошо демонстрирует возможности динамической визуализации объектов при помощи языка R. Возможно, я неверно интерпретировал некоторые действия, неверно назвал те или иные объекты, однако, я не специалист в данной области и изучаю это всего месяц. Но уже сейчас могу сказать, что без знания данного языка и без умения использовать данные инструменты далеко в исследованиях не продвинешься.
©Arik Taranis